package net.orange.architecture.model;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;

import javax.persistence.Column;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;



/**
 * The base value object which can be persistence in database, every business
 * object should be extended from the base value object.
 */
@MappedSuperclass
public abstract class BaseValueObject implements Serializable, Cloneable {

	private static final long serialVersionUID = -3653229469768029941L;

	/**
	 * The primaray of the object
	 */
	@Id
	@Column(name = "pk", unique = true, nullable = false)
	public long pk;

	/**
	 * @return Returns the pk.
	 */
	public long getPk() {
		return pk;
	}

	/**
	 * @param pk
	 *            The pk to set.
	 */
	public void setPk(long pk) {
		this.pk = pk;
	}

	/**
	 * Validate the object is satisfy the specify criteria.
	 * 
	 * @param msg
	 *            The error message return.
	 * @return Whether pass the validation.
	 * @throws Exception
	 */
	public boolean satisfy(Collection<String> msg) throws Exception {
		if (msg != null)
			msg = new ArrayList();

		msg.clear();

		return true;
	}

	/**
	 * Validation whether the object can be removed.
	 * 
	 * @param msg
	 *            The error message return.
	 * @return Whether the object can be removed.
	 * @throws Exception
	 */
	public boolean canRemove(Collection<String> msg) throws Exception {
		if (msg != null)
			msg = new ArrayList();

		msg.clear();

		return true;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#clone()
	 */
	public Object clone() throws CloneNotSupportedException {
		Object cloneObject = null;

		// 1.initialize a new object.
		try {
			cloneObject = getClass().newInstance();
		} catch (IllegalAccessException e) {
			throw new CloneNotSupportedException(e.getMessage());
		} catch (InstantiationException e) {
			throw new CloneNotSupportedException(e.getMessage());
		}

		// 2.Get the property description
		PropertyDescriptor descriptors[] = null;

		try {
			BeanInfo beanInfo = Introspector.getBeanInfo(getClass());
			descriptors = beanInfo.getPropertyDescriptors();
		} catch (IntrospectionException e) {
			throw new CloneNotSupportedException(e.getMessage());
		}

		// 3.Set the properties value
		for (PropertyDescriptor descriptor : descriptors) {
			Method readMethod = descriptor.getReadMethod();
			Method writeMethod = descriptor.getWriteMethod();
			try {
				Object sourceValue = readMethod.invoke(this, new Object[0]);

				if (readMethod != null & writeMethod != null
						&& !(sourceValue instanceof Collection)
						&& !(sourceValue instanceof Map)) {

					Object[] targetValue = new Object[1];
					targetValue[0] = sourceValue;
					writeMethod.invoke(cloneObject, targetValue);
				}

			} catch (IllegalArgumentException e) {
				throw new CloneNotSupportedException(e.getMessage());
			} catch (IllegalAccessException e) {
				throw new CloneNotSupportedException(e.getMessage());
			} catch (InvocationTargetException e) {
				throw new CloneNotSupportedException(e.getMessage());
			}

		}

		return cloneObject;
	}
}